package com.syh.jn.scm.domain.supplier.entity.finance;

import cn.redscarf.commons.util.bean.ExtBeanUtils;
import cn.redscarf.commons.util.exception.IllegalParameterException;
import cn.redscarf.commons.util.other.ExtAssert;
import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.util.validator.ValidationUtils;
import com.syh.core.ddd.domain.AbstractSimpleEntityObject;
import com.syh.jn.scm.domain.supplier.entity.logistics.LogisticsBill;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseOrder;
import com.syh.jn.scm.domain.supplier.entity.purchase.PurchaseReturnOrder;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleOrder;
import com.syh.jn.scm.domain.supplier.entity.sale.SaleReturnOrder;
import com.syh.jn.scm.domain.supplier.repository.finance.CostBillReadRepository;
import com.syh.jn.scm.domain.supplier.repository.finance.CostBillRepository;
import com.syh.jn.scm.domain.supplier.repository.finance.IncomeBillReadRepository;
import com.syh.jn.scm.domain.supplier.repository.finance.IncomeBillRepository;
import com.syh.jn.scm.domain.supplier.repository.logistics.LogisticsBillReadRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurReturnOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurchaseOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.purchase.PurchaseReturnOrderRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleReturnOrderReadRepository;
import com.syh.jn.scm.domain.supplier.repository.sale.SaleReturnOrderRepository;
import com.syh.jn.scm.dto.supplier.finance.statementAccountBill.StatementAccountBillMto;
import com.syh.jn.scm.dto.supplier.finance.statementAccountBill.StatementAccountBillSto.StatementAccountBillItemSto;
import com.syh.jn.scm.exception.BusinessException;
import com.syh.jn.scm.service.api.supplier.finance.enums.CostBillEnums;
import com.syh.jn.scm.service.api.supplier.finance.enums.IncomeBillEnum;
import com.syh.jn.scm.service.api.supplier.finance.enums.StatementAccountBillEnum;
import com.syh.jn.scm.service.api.supplier.logistics.enums.LogisticsBillEnums;
import com.syh.jn.scm.service.api.supplier.purchase.enums.PurchaseOrderEnums;
import com.syh.jn.scm.service.api.supplier.purchase.enums.PurchaseReturnOrderEnums;
import com.syh.jn.scm.service.api.supplier.sale.enums.SaleOrderEnums;
import com.syh.jn.scm.service.api.supplier.sale.enums.SaleReturnOrderEnums;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.UUID;

/**
 * 账单项
 */
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class StatementAccountBillItem extends AbstractSimpleEntityObject<Long> {

  /**
   * 生成对账单项 每张单的数据逻辑校验分配给每个项在创建的时候自己去匹配 达到职责分离
   *
   * @param sto
   * @param customerType
   * @param customerId
   */
  StatementAccountBillItem(StatementAccountBillItemSto sto, Short customerType, Long customerId) {
    ValidationUtils.validate(sto);
    ExtBeanUtils.copyProperties(sto, this);
    this.setUuid(UUID.randomUUID().toString());
    this.createdItemTotalAmount(customerType,customerId,false);
  }
  StatementAccountBillItem(StatementAccountBillMto.StatementAccountBillItemMto mto, Short customerType, Long customerId) {
    ValidationUtils.validate(mto);
    ExtBeanUtils.copyProperties(mto, this);
    this.setUuid(UUID.randomUUID().toString());
    this.createdItemTotalAmount(customerType,customerId,true);
  }

  /**
   * 生成订单项对账金额
   * @param customerType
   * @param customerId
   */
  private void createdItemTotalAmount(Short customerType, Long customerId, boolean isUpdata) {
    // 上游   获取的是订单价格 上游只有采购端相关
    if (StatementAccountBillEnum.CUSTOMER_TYPE_UPSTREAM.getStatus().equals(customerType) || StatementAccountBillEnum.CUSTOMER_TYPE_DOWNSTREAM.getStatus().equals(customerType)) {
      switch (this.getSourceType().intValue()) {
        case 2:
          PurchaseOrder purchaseOrd = this.getPurchaseOrd(customerId, isUpdata);
          this.totalAmount = purchaseOrd.getTotalInAmount();
          break;
        case 3:
          PurchaseReturnOrder purchaseReturnOrd = this.getPurchaseReturnOrd(customerId, isUpdata);
          this.totalAmount = purchaseReturnOrd.getReturnTotalOutAmount();
          break;
      }
    }
    // 下游  获取的是订单价格 下游只有销售相关
    if (StatementAccountBillEnum.CUSTOMER_TYPE_DOWNSTREAM.getStatus().equals(customerType) || Short.valueOf("1").equals(customerType)) {
      switch (this.getSourceType().intValue()) {
        case 0:
          SaleOrder saleOrder = this.getSaleOrd(customerId, isUpdata);
          this.totalAmount = saleOrder.getTotalOutAmount();
          break;
        case 1:
          SaleReturnOrder saleReturnOrd = this.getSaleReturnOrd(customerId, isUpdata);
          this.totalAmount = saleReturnOrd.getReturnTotalInAmount();
          break;
      }
    }
    // 物流客户对账  获取的是物流费用
    if (StatementAccountBillEnum.CUSTOMER_TYPE_LOGISTICS.getStatus().equals(customerType)) {
      if (this.getSourceType().equals(StatementAccountBillEnum.SOURCE_LIGISTICS_BILL.getStatus())) {
        LogisticsBill logisticsBill = this.getLogisticsBill(customerId, isUpdata);
        this.totalAmount = logisticsBill.getTotalFreight();
      }
    }
    // 费用单与收入单一致
    switch (this.getSourceType().intValue()) {
      case 4:
        CostBill costBill = this.getCostBill(customerId, isUpdata);
        this.totalAmount = costBill.getPrice();
        break;
      case 5:
        IncomeBill incomeBill = this.getIncomeBill(customerId, isUpdata);
        this.totalAmount = incomeBill.getPrice();
        break;
    }

    // 出现为空的情况只有单据类型与客户类型没对上 或者原始单据金额就发生错误了
    if (this.totalAmount == null) {
      throw new IllegalParameterException("非法参数，可能传入与供应商对账有销售单数据");
    }
  }

  /**
   * 处理销售订单
   *
   * @return
   */
  private SaleOrder getSaleOrd(Long customerId, boolean isUpdata) {
    ExtAssert.isTrue(this.getSourceType() == StatementAccountBillEnum.SOURCE_SALE_ORD.getStatus(), "类型错误");
    SaleOrderReadRepository saleOrderReadRepository = (SaleOrderReadRepository) SpringContextUtils.getBean("saleOrderRepositoryImpl");
    SaleOrder saleOrder = saleOrderReadRepository.getById(this.getSourceId());

    //判断是新增还是更新
    if (isUpdata) {
      if (!saleOrder.getOrdState().equals(SaleOrderEnums.State.STATEMENT.getStatus()) && !saleOrder.getOrdState().equals(SaleOrderEnums.State.CONFIRM.getStatus())) {
        throw new BusinessException(saleOrder.getCode() + "，单据未在确认状态或未生成对账单！");
      }
    }
    else {
      if (!saleOrder.getOrdState().equals(SaleOrderEnums.State.CONFIRM.getStatus())) {
        throw new BusinessException(saleOrder.getCode() + "，单据未在确认状态！");
      }
    }

    if (!saleOrder.getCode().equals(this.getSourceCode())) {
      throw new IllegalParameterException("单据编号与id无法对齐");
    }
    return saleOrder;
  }

  /**
   * 处理销售退货单
   *
   * @return
   */
  private SaleReturnOrder getSaleReturnOrd(Long customerId, boolean isUpdata) {
    ExtAssert.isTrue(this.getSourceType() == StatementAccountBillEnum.SOURCE_SALE_RETURN_ORD.getStatus(), "类型错误");
    SaleReturnOrderReadRepository saleReturnOrderReadRepository = (SaleReturnOrderReadRepository) SpringContextUtils.getBean(SaleReturnOrderRepository.class);
    SaleReturnOrder saleReturnOrder = saleReturnOrderReadRepository.getById(this.getSourceId());

    if (isUpdata) {
      if (!saleReturnOrder.getReturnOrdState().equals(SaleReturnOrderEnums.State.STATEMENT.getStatus()) && !saleReturnOrder.getReturnOrdState().equals(SaleReturnOrderEnums.State.CONFIRM.getStatus())) {
        throw new BusinessException(saleReturnOrder.getCode() + "，单据未在确认状态或未生成对账单！");
      }
    }
    else {
      if (!saleReturnOrder.getReturnOrdState().equals(SaleReturnOrderEnums.State.CONFIRM.getStatus())) {
        throw new BusinessException(saleReturnOrder.getCode() + "，单据未在确认状态！");
      }
    }

    if (!saleReturnOrder.getCode().equals(this.getSourceCode())) {
      throw new IllegalParameterException("单据编号与id无法对齐");
    }

    return saleReturnOrder;
  }

  /**
   * 处理采购订单
   *
   * @return
   */
  private PurchaseOrder getPurchaseOrd(Long customerId, boolean isUpdata) {
    ExtAssert.isTrue(this.getSourceType() == StatementAccountBillEnum.SOURCE_PURCHASE_ORD.getStatus(), "类型错误");
    PurchaseOrderReadRepository purchaseOrderReadRepository = (PurchaseOrderReadRepository) SpringContextUtils.getBean("purchaseOrderRepositoryImpl");
    PurchaseOrder purchaseOrder = purchaseOrderReadRepository.getById(this.getSourceId());

    if (isUpdata) {
      if (!PurchaseOrderEnums.OrdState.CONFIRM.getStatus().equals(purchaseOrder.getOrdState()) && !PurchaseOrderEnums.OrdState.STATEMENT.getStatus().equals(purchaseOrder.getOrdState())) {
        throw new BusinessException(purchaseOrder.getCode() + "，单据未在确认状态或未生成对账单！");
      }
    }
    else {
      if (!PurchaseOrderEnums.OrdState.CONFIRM.getStatus().equals(purchaseOrder.getOrdState())) {
        throw new BusinessException(purchaseOrder.getCode() + "，单据未在确认状态！");
      }
    }

    if (!purchaseOrder.getCode().equals(this.getSourceCode())) {
      throw new IllegalParameterException("单据编号与id无法对齐");
    }

    return purchaseOrder;
  }


  /**
   * 处理采购订单
   *
   * @return
   */
  private LogisticsBill getLogisticsBill(Long customerId, boolean isUpdata) {
    ExtAssert.isTrue(this.getSourceType() == StatementAccountBillEnum.SOURCE_LIGISTICS_BILL.getStatus(), "类型错误");
    LogisticsBillReadRepository logisticsBillReadRepository = (LogisticsBillReadRepository) SpringContextUtils.getBean(LogisticsBillReadRepository.class);
    LogisticsBill logisticsBill = logisticsBillReadRepository.getById(this.getSourceId());

    if (isUpdata) {
      if (logisticsBill.getStatementState() == null) {
        throw new BusinessException(logisticsBill.getCode() + "单据无法生成对账单");
      }
    }
    else {
      if (!LogisticsBillEnums.Statement.NOT_STATEMENT.getStatus().equals(logisticsBill.getStatementState())) {
        throw new BusinessException(logisticsBill.getCode() + "，单据未在未生成对账单状态！");
      }
    }

    if (!logisticsBill.getCode().equals(this.getSourceCode())) {
      throw new IllegalParameterException("单据编号与id无法对齐");
    }
    if (!LogisticsBillEnums.State.SIGNIN.getStatus().equals(logisticsBill.getLogisticsState())) {
      throw new BusinessException(logisticsBill.getCode() + "，单据未在已收货状态");
    }

    return logisticsBill;
  }

  /**
   * 处理采购退货单
   *
   * @return
   */
  private PurchaseReturnOrder getPurchaseReturnOrd(Long customerId, boolean isUpdata) {
    ExtAssert.isTrue(this.getSourceType() == StatementAccountBillEnum.SOURCE_PURCHASE_RETURN_ORD.getStatus(), "类型错误");
    PurReturnOrderReadRepository purReturnOrderReadRepository = (PurReturnOrderReadRepository) SpringContextUtils.getBean(PurchaseReturnOrderRepository.class);
    PurchaseReturnOrder purchaseReturnOrder = purReturnOrderReadRepository.getById(this.getSourceId());

    if (isUpdata) {
      if (!PurchaseReturnOrderEnums.OrdState.CONFIRM.getStatus().equals(purchaseReturnOrder.getReturnOrdState()) && !PurchaseReturnOrderEnums.OrdState.STATEMENT.getStatus().equals(purchaseReturnOrder.getReturnOrdState())) {
        throw new BusinessException(purchaseReturnOrder.getReturnCode() + "，单据未在确认状态或未生成对账单！");
      }
    }
    else {
      if (!PurchaseReturnOrderEnums.OrdState.CONFIRM.getStatus().equals(purchaseReturnOrder.getReturnOrdState())) {
        throw new BusinessException(purchaseReturnOrder.getReturnCode() + "，单据未在确认状态！");
      }
    }

    if (!purchaseReturnOrder.getReturnCode().equals(this.getSourceCode())) {
      throw new IllegalParameterException("单据编号与id无法对齐");
    }

    return purchaseReturnOrder;
  }

  /**
   * 处理费用单
   *
   * @return
   */
  private CostBill getCostBill(Long customerId, boolean isUpdata) {
    ExtAssert.isTrue(this.getSourceType() == StatementAccountBillEnum.SOURCE_COST_BILL.getStatus(), "类型错误");
    CostBillReadRepository costBillReadRepository = (CostBillReadRepository) SpringContextUtils.getBean(CostBillRepository.class);
    CostBill costBill = costBillReadRepository.getById(this.getSourceId());

    ExtAssert.isTrue(costBill.getCustomerId().equals(customerId), costBill.getCode() + "，单据归属错误！");

    if (isUpdata) {
      if (!CostBillEnums.State.CONFIRM.getStatus().equals(costBill.getState()) && !CostBillEnums.State.STATEMENT.getStatus().equals(costBill.getState())) {
        throw new BusinessException(costBill.getCode() + "，单据未在确认状态或未生成对账单！");
      }
    }
    else {
      if (!CostBillEnums.State.CONFIRM.getStatus().equals(costBill.getState())) {
        throw new BusinessException(costBill.getCode() + "，单据未在确认状态！");
      }
      if (costBill.getStatementOfAccountId() != null) {
        throw new BusinessException(costBill.getCode() + "，单据已经生成对账单！");
      }
    }


    if (!costBill.getCode().equals(this.getSourceCode())) {
      throw new IllegalParameterException("单据编号与id无法对齐");
    }
    if (!costBill.getConfirmState().equals(CostBillEnums.ConfirmState.CONFIRM.getStatus())) {
      throw new BusinessException(costBill.getCode() + "，对方仍未确认费用单有效");
    }
    return costBill;
  }

  /**
   * 处理收入单
   *
   * @return
   */
  private IncomeBill getIncomeBill(Long customerId, boolean isUpdata) {
    ExtAssert.isTrue(this.getSourceType() == StatementAccountBillEnum.SOURCE_INCOME_BILL.getStatus(), "类型错误");
    IncomeBillReadRepository incomeBillReadRepository = (IncomeBillReadRepository) SpringContextUtils.getBean(IncomeBillRepository.class);
    IncomeBill incomeBill = incomeBillReadRepository.getById(this.getSourceId());

    ExtAssert.isTrue(incomeBill.getCustomerId().equals(customerId), incomeBill.getCode() + "，单据归属错误！");

    if (isUpdata) {
      if (!IncomeBillEnum.CONFIRM.getStatus().equals(incomeBill.getState()) && !IncomeBillEnum.STATEMENT.getStatus().equals(incomeBill.getState())) {
        throw new BusinessException(incomeBill.getCode() + "，单据未在确认状态或未生成对账单！");
      }
    }
    else {
      if (!IncomeBillEnum.CONFIRM.getStatus().equals(incomeBill.getState())) {
        throw new BusinessException(incomeBill.getCode() + "，单据未在确认状态！");
      }
      if (incomeBill.getStatementOfAccountId() != null) {
        throw new BusinessException(incomeBill.getCode() + "，单据已经生成对账单！");
      }
    }

    if (!incomeBill.getCode().equals(this.getSourceCode())) {
      throw new IllegalParameterException("单据编号与id无法对齐");
    }
    if (incomeBill.getConfirmState() != IncomeBillEnum.OTHERPARTY_CONFIRM.getStatus()) {
      throw new BusinessException(incomeBill.getCode() + "，对方仍未确认费用单有效");
    }
    return incomeBill;
  }

  /**
   * 对账单id
   */
  private Long statementAccountBillId;
  /**
   * 来源单类型 0 销售单 1 销售退货单 2采购单 4 采购退货单 5 费用单 6 收入单
   */
  private Short sourceType;
  /**
   * 来源单号
   */
  private String sourceCode;
  /**
   * 来源单id
   */
  private Long sourceId;
  /**
   * 单据总额
   */
  private BigDecimal totalAmount;
}
